package org.jradweb.tools.generator.freemarker;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.apache.maven.plugin.logging.Log;
import org.jradweb.tools.generator.FileAlreadyExistsException;
import org.jradweb.tools.generator.FileOperatorException;
import org.jradweb.tools.generator.FreemarkerTemplateExcepion;
import org.jradweb.tools.generator.freemarker.util.FileGenerator;
import org.jradweb.tools.generator.freemarker.util.Templates;
import org.jradweb.tools.generator.utils.LogImpl;

public class PageGenerator extends ClassGenerator {
	private String pagePath;
	private String classPath;

	public PageGenerator(File projectRootPath, String groupId,
			String artifactId, Log log) {
		super(projectRootPath, groupId, artifactId, log);

		pagePath = projectRootPath.getAbsolutePath()
				+ "/src/main/webapp/WEB-INF/jsp";
		classPath = projectRootPath.getAbsolutePath() + "/target/classes/";
		this.params.put("sy", "${");
	}

	public boolean createIndextPage(String controllerName, String modelName,
			boolean force) throws FileOperatorException,
			FileAlreadyExistsException, FreemarkerTemplateExcepion {
		Value model = new Value(modelName);

		List<ModelProperty> columns = this.getProperties(model);

		Value controller = new Value(controllerName);
		params.put("model", model);
		params.put("columns", columns);

		File indexPageFile = new File(this.pagePath + "/"
				+ controller.getLowerCaseFirst() + "/index.jsp");

		try {
			FileGenerator.writeFile(indexPageFile, params,
					Templates.JSP_INDEX_TEMPLATE, force);
		} catch (FileAlreadyExistsException e) {
			log.warn("File " + indexPageFile.getAbsolutePath()
					+ " already exists!");
			return false;
		}

		log.info("File " + indexPageFile.getAbsolutePath()
				+ " created successfully!");

		return true;
	}

	public boolean createCreatePage(String controllerName, String modelName,
			boolean force) throws FileOperatorException,
			FileAlreadyExistsException, FreemarkerTemplateExcepion {
		Value model = new Value(modelName);

		List<ModelProperty> columns = this.getEditProperties(model);

		Value controller = new Value(controllerName);
		params.put("model", model);
		params.put("items", this.toMultiColumn(columns, 2));

		File createPageFile = new File(this.pagePath + "/"
				+ controller.getLowerCaseFirst() + "/create.jsp");

		try {
			FileGenerator.writeFile(createPageFile, params,
					Templates.JSP_CREATE_TEMPLATE, force);
		} catch (FileAlreadyExistsException e) {
			log.warn("File " + createPageFile.getAbsolutePath()
					+ " already exists!");
			return false;
		}

		log.info("File " + createPageFile.getAbsolutePath()
				+ " created successfully!");

		return true;
	}

	public boolean createEditPage(String controllerName, String modelName,
			boolean force) throws FileOperatorException,
			FileAlreadyExistsException, FreemarkerTemplateExcepion {
		Value model = new Value(modelName);

		List<ModelProperty> columns = this.getEditProperties(model);

		Value controller = new Value(controllerName);
		params.put("model", model);
		params.put("items", this.toMultiColumn(columns, 2));

		File createPageFile = new File(this.pagePath + "/"
				+ controller.getLowerCaseFirst() + "/edit.jsp");

		try {
			FileGenerator.writeFile(createPageFile, params,
					Templates.JSP_EDIT_TEMPLATE, force);
		} catch (FileAlreadyExistsException e) {
			log.warn("File " + createPageFile.getAbsolutePath()
					+ " already exists!");
			return false;
		}

		log.info("File " + createPageFile.getAbsolutePath()
				+ " created successfully!");

		return true;
	}

	public boolean createShowPage(String controllerName, String modelName,
			boolean force) throws FileOperatorException,
			FileAlreadyExistsException, FreemarkerTemplateExcepion {
		Value model = new Value(modelName);

		List<ModelProperty> columns = this.getProperties(model);

		Value controller = new Value(controllerName);
		params.put("model", model);
		params.put("items", this.toMultiColumn(columns, 2));

		File createPageFile = new File(this.pagePath + "/"
				+ controller.getLowerCaseFirst() + "/show.jsp");

		try {
			FileGenerator.writeFile(createPageFile, params,
					Templates.JSP_SHOW_TEMPLATE, force);
		} catch (FileAlreadyExistsException e) {
			log.warn("File " + createPageFile.getAbsolutePath()
					+ " already exists!");
			return false;
		}

		log.info("File " + createPageFile.getAbsolutePath()
				+ " created successfully!");

		return true;
	}

	private <T> List<List<T>> toMultiColumn(List<T> values, int columnNumber) {
		List<List<T>> mvalues = new ArrayList<List<T>>();

		for (int i = 0; i < values.size(); i++) {
			T v = values.get(i);
			if (mvalues.isEmpty()) {
				List<T> vs = new ArrayList<T>();
				vs.add(v);

				mvalues.add(vs);
			} else {
				List<T> mv = mvalues.get(mvalues.size() - 1);
				boolean added = false;

				if (mv.size() < columnNumber) {
					mv.add(v);
				} else {
					List<T> vs = new ArrayList<T>();
					vs.add(v);

					mvalues.add(vs);
				}

			}

		}

		return mvalues;
	}

	/*
	 * return properties need show one edit and create pages
	 */
	private List<ModelProperty> getEditProperties(Value model) {
		List<ModelProperty> mps = new ArrayList<ModelProperty>();
		List<ModelProperty> allMps = this.getProperties(model);
		for (ModelProperty mp : allMps) {
			if (mp.getPropertyType().equals(ModelProperty.PROPERTY_TYPE_COLUMN)) {
				mps.add(mp);
			} else if (mp.getPropertyType().equals(
					ModelProperty.PROPERTY_TYPE_MANY_TO_ONE)) {
				mps.add(mp);
			}
		}

		return mps;
	}

	public List<ModelProperty> getModelProperties(Class modelClass)
			throws ClassNotFoundException {
		List<ModelProperty> values = new ArrayList<ModelProperty>();
		for (Method method : modelClass.getMethods()) {
			if (method.getAnnotation(Column.class) != null) {
				if (!method.getName().equals("getCreatedAt")
						&& !method.getName().equals("getUpdatedAt")) {
					Column column = method.getAnnotation(Column.class);
					String name = method.getName();
					if (name.length() > 3 && name.substring(0, 3).equals("get")) {
						name = method.getName().substring(3);
					}
					else if(name.length()>2 &&name.substring(0, 2).equals("is")){
						name=name.substring(2);
					}

					ModelProperty mp = new ModelProperty(name);

					mp.setPropertyType(ModelProperty.PROPERTY_TYPE_COLUMN);
					mp.setPropertyClass(method.getReturnType());
					mp.setColumnName(column.name());
					values.add(mp);
				}
			} else if (method.getAnnotation(ManyToOne.class) != null) {
				String name = method.getName().substring(3);
				JoinColumn joinColumn = method.getAnnotation(JoinColumn.class);
				ModelProperty mp = new ModelProperty(name);

				mp.setPropertyType(ModelProperty.PROPERTY_TYPE_MANY_TO_ONE);
				mp.setPropertyClass(method.getReturnType());
				if (joinColumn != null)
					mp.setColumnName(joinColumn.name());
				else
					mp.setColumnName(mp.getLowerCaseFirst() + "_id");

				values.add(mp);
			} else if (method.getAnnotation(OneToMany.class) != null) {
				String name = method.getName().substring(3);

				ModelProperty mp = new ModelProperty(name);

				mp.setPropertyType(ModelProperty.PROPERTY_TYPE_ONE_TO_MANY);
				Type type = method.getGenericReturnType();
				int start = type.toString().indexOf("<") + 1;
				int end = type.toString().indexOf(">");
				String className = type.toString().substring(start, end);

				Class propertyModelClass = Thread.currentThread()
						.getContextClassLoader().loadClass(className);
				mp.setPropertyClass(propertyModelClass);
				mp.setListItemName(new Value(className.substring(className
						.lastIndexOf(".") + 1)));

				values.add(mp);
			}
		}
		return values;
	}

	private List<ModelProperty> getProperties(Value model) {
		URL url = null;
		Class modelClass = null;
		List<ModelProperty> columns = new ArrayList<ModelProperty>();
		try {
			url = new URL("file:" + classPath);

			ClassLoader loader = Thread.currentThread().getContextClassLoader();
			URLClassLoader ucl = new URLClassLoader(new URL[] { url }, loader);

			String className = this.groupId + "." + this.artifactId + ".model."
					+ model.getName();
			modelClass = ucl.loadClass(className);
			columns = this.getModelProperties(modelClass);
		} catch (MalformedURLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return columns;
	}

	private List<Value> getProperties(Class modelClass) {
		List<Value> values = new ArrayList<Value>();
		for (Method method : modelClass.getMethods()) {
			if (method.getAnnotation(Column.class) != null) {
				if (!method.getName().equals("getCreatedAt")
						&& !method.getName().equals("getUpdatedAt")) {
					String name = method.getName().substring(3);
					values.add(new Value(name));
				}
			}
		}
		return values;
	}

	public static void main(String[] args) throws Exception {

		String model = "Lead";

		File projectRootPath = new File("D:/workspace2/easycrm");
		PageGenerator cg = new PageGenerator(projectRootPath,
				"com.infowarelab", "easycrm", new LogImpl(ClassGenerator.class));
		cg.createIndextPage(model, model, true);

		cg.createCreatePage(model, model, true);
		// cg.createModelClass(model);
		// cg.createDao(model);
		// cg.createDaoImpl(model);
		// cg.createService(model, model,true);
		// cg.createAbstractService(model, model,true);
		// cg.createServiceImpl(model, model, true);
		// cg.createController(model, model, model,true);
		//			
		// cg.createIndextPage(model, model, true);
		// cg.createCreatePage(model, model, true);
		// cg.createEditPage(model, model, true);
		// cg.createShowPage(model, model, true);

	}

}
